//实现3个核心功能


//1.点击轮播图左右的箭头,控制轮播图播放

//2.点击轮播图下方的指示器,控制轮播图播放

//3.自动轮播


//轮播图原理: 用动画函数控制整个wrap盒子的移动,每次移动一张图片的宽度


//使用面向对象的方式来完成这个轮播图


//定义两个类

//第一个类是一个工具类,工具类中有一些方法能够给我们方便的使用比如:操作样式 获取元素 创建元素

class Tools {
    //获取元素
    //jsDOC
    /**
     * @param {String} selector 选择元素的选择器
     * @returns {Element} 返回一个获取的元素
     */
    $ = selector => document.querySelector(selector);
    //创建元素
    /**
     * 
     * @param {String} tagName 创建元素的元素名
     * @returns {Node} 一个创建好的节点
     */
    _c = tagName => document.createElement(tagName);
    //设定样式
    /**
     * 
     * @param {Element} el 要设定样式的元素
     * @param {Object} styleObj 要设定的样式
     */
    _set = (el, styleObj) => {
        for (let k in styleObj) {
            el.style[k] = styleObj[k]
        }
    }
}


//第二个类是轮播图核心类,里面有控制轮播图的方法

class Carousel extends Tools {
    constructor() {
        super()
        //在这里面使用let,说明这个属性只在当前的函数中使用
        //用this 表示将属性添加到实力上,任何一个实例的方法都能得到这个属性
        //获取轮播图ul容器
        this.wrap = this.$(".wrap");
        //一张图的宽度是多少
        this.mask = this.$('.mask');
        this.onePicWidth = this.mask.offsetWidth;
        //图片的索引
        this.picIndex = 0;
        //获取控制轮播图的箭头
        this.arrow = this.$('.arrow');
        // 左边的箭头
        this.leftArr = this.arrow.children[0];
        // 右边的箭头
        this.rightArr = this.arrow.children[1];
        // console.log(this.arrow)
        //获取指示器容器元素
        this.indicator = this.$('.indicator');
        // console.log(this.indicator)
        //声明一个用于判断指示器是否显示的属性
        this.isShowIndicator = false; //默认没有指示器
        //开启无缝轮播需要对图片进行克隆,再声明一个控制无缝轮播的熟悉
        this.isCircular = false;
        //动态的计算wrap的宽度
        this._set(this.wrap, { width: this.wrap.children.length * this.onePicWidth + "px" });
        //一个属性用于判断是否开启了自动轮播
        this.isAutoplay = false;
    }

    //定义让图片运动的方法
    slider = () => {
        animate(this.wrap, -(this.onePicWidth * this.picIndex))
    }

    //定义鼠标事件
    bindMouseEvent = () => {
        let _this = this;//使用_this来存储当前实例的this
        //鼠标移入
        this.mask.onmouseenter = function () {
            //传统函数的this指向事件源
            _this._set(_this.arrow, { display: 'block' })
            //如果开启了自动轮播,要清除定时器
            // console.log(_this.isAutoplay)
            // console.log(_this.interval)
            if (_this.interval && _this.isAutoplay) {
                clearInterval(_this.interval)
            }
        }
        //鼠标离开
        this.mask.onmouseleave = () => {
            this._set(this.arrow, { display: 'none' });
            //鼠标离开需要继续轮播
            //判断是否开启自动轮播
            if (this.isAutoplay) {
                this.play()
            }
        }

        //点击左右两个箭头的事件

        this.leftArr.addEventListener('click', () => {
            this.prev()
        })

        this.rightArr.addEventListener('click', () => {
            this.next()
        })

    }

    //细化出两个方法  
    //一个是上一张
    prev = () => {
        //控制picIndex ++
        this.picIndex--

        if (this.picIndex < 0) {
            if (this.isCircular) {
                //手动将轮播图放到最后一张
                this._set(this.wrap, { left: -(this.wrap.children.length - 1) * this.onePicWidth + "px" });
                //更改picIndex
                this.picIndex = this.wrap.children.length - 2;
            } else {
                this.picIndex = this.wrap.children.length - 1
            }
        }

        this.slider()

        if (this.isShowIndicator) {
            //让指示器高亮的方法
            this.indicatorActive()
        }
    }
    //一个是下一张
    next = () => {
        //控制picIndex --
        //有缝轮播,播到最后一张的时候后,让轮播图倒车回到第一张
        this.picIndex++
        if (this.picIndex === this.wrap.children.length) {
            //判断是否开启无缝轮播
            if (this.isCircular) {
                //手动将wrap的位置放到 left:0的状态
                this._set(this.wrap, { left: 0 });
                //将pickIndex变成1
                this.picIndex = 1;
            } else {
                this.picIndex = 0;
            }
        }
        //调用图片运动的逻辑
        this.slider()

        if (this.isShowIndicator) {
            //让指示器高亮的方法
            this.indicatorActive()
        }
    }

    //控制指示器

    showIndicator() {
        //让判断指示器是否显示的属性变成true
        this.isShowIndicator = true;
        //让指示器显示
        //动态添加指示器,有几张图就有几个指示器,this.wrap.children.length;

        let len;
        if (this.isCircular) { //防止指示器和无缝轮播调用循序先后造成指示器多一个的问题
            len = this.wrap.children.length - 1
        } else {
            len = this.wrap.children.length
        }

        //通过循环创建指示器

        for (var i = 0; i < len; i++) {
            let li = this._c('li');//创建li元素
            li.innerText = i + 1
            this.indicator.appendChild(li);
        }

        //通过事件委托的方式 给指示器每个元素注册事件

        this.indicator.addEventListener('click', (e) => {
            // console.log(e.target)
            if (e.target.localName === "li") {
                let that = e.target;//保存事件源
                //更改了picIndex
                this.picIndex = that.innerText - 1;
                //让图片滑动    
                this.slider()
                //调用让指示器高亮的方法
                this.indicatorActive()
            }
        })
        //让指示器的盒子显示出来
        this._set(this.indicator, { display: 'block' })
        //首次显示指示器的时候,也需要让指示器进行高亮显示
        this.indicatorActive()
    }
    //指示器激活方法
    indicatorActive() {
        //需要知道点击了哪个指示器
        let indicatorIndex = this.picIndex;
        //让点击了的指示器加上active这个类名 ,其它的指示器去掉active类名 排他思想

        // console.log(this.indicator.children)
        //获取所有的指示器按钮
        let indicators = this.indicator.children;
        // console.log(indicatorIndex)
        //给每个指示器注册事件(重复操作,代码冗余)

        // for (var i = 0; i < indicators.length; i++) {
        //     indicators[i].onclick = function () {

        //     }
        // }

        //只需要处理排他就行了

        for (var i = 0; i < indicators.length; i++) {
            indicators[i].className = ""
        }
        //让点击的指示器加上active类名

        //判断是否开启了无缝轮播 并且因为指示器的长度只有克隆之前轮播图的长度那么多,所以到最后一张的时候后报错了,因为指示器少了一个
        if (this.isCircular && indicatorIndex === this.wrap.children.length - 1) {
            indicatorIndex = 0
        }

        // console.log(indicators)
        indicators[indicatorIndex].className = "active"
    }
    //一个方法控制无缝轮播的逻辑;

    circular() {
        //将this.isCircular属性标记为true
        this.isCircular = true;
        //克隆轮播图中的第一个元素,放到最后
        //克隆元素的语法:xxx.cloneNode(true) true表示深度克隆
        //深度克隆,连同元素的子节点一起被克隆
        let firstNode = this.wrap.children[0];

        let lastNode = firstNode.cloneNode(true);

        //将克隆的lastNode放到wrap容器的最后

        this.wrap.appendChild(lastNode)

        //重新计算wrap容器的宽度

        this._set(this.wrap, { width: this.wrap.children.length * this.onePicWidth + "px" })
    }

    //定义一个播放的方法
    play(delay = 1000) {
        // console.log(delay)

        this.interval = setInterval(() => {
            this.next()
        }, delay)
    }

    autoplay(delay) {
        //开启无缝轮播的判断属性
        this.isAutoplay = true;
        //首次调用自动轮播的方法
        this.play(delay)
        //窗口获得焦点 重新轮播
        window.onfocus = () => {
            // console.log('浏览器获得了焦点')
            // console.log(delay)
            this.play(delay)
        }
        //窗口失去焦点 清除轮播定时器
        window.onblur = () => {
            // console.log('浏览器失去了焦点')
            if (this.interval) { //如果设置过了定时器,那么就清除
                clearInterval(this.interval)
            }
        }
    }
}


let c = new Carousel()

//面向对象的三大特性
//1.封装 无需关注对象中的方法具体实现逻辑是什么,只需要会使用对象对外暴露的方法就可以了

//2.继承

//3.多态

c.bindMouseEvent()//绑定鼠标事件
c.circular() //开启无缝轮播
c.showIndicator() //显示指示器
c.autoplay(1000) //自动轮播
console.log(c)